/*
Copyright DAUPHIN Maxime 2005

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
http://www.gnu.org/licenses/gpl.txt
*/
#include <assert.h>
#include "Wnd.h"
#include "Dc.h"

namespace Gui {

std::map<HWND,Wnd*> Wnd::m_Wnds;

HINSTANCE Wnd::g_hInstance = 0;

/** contructor */
Wnd::Wnd(void)
{
	m_hWnd = NULL;
	m_x = CW_USEDEFAULT;
	m_y = CW_USEDEFAULT;
	m_width = 400;
	m_height = 300;
	m_className = _T("ClassName");
	m_windowName = _T("No Title");
	m_style = WS_OVERLAPPEDWINDOW;
	m_pParent = NULL;
	m_hWndNextViewer = NULL;

    m_wc.style = 0;
	m_wc.lpfnWndProc = globalWndProc;
    m_wc.cbClsExtra = 0;
    m_wc.cbWndExtra = 0;
    m_wc.hInstance = NULL;
    m_wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    m_wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    m_wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
    m_wc.lpszMenuName =  NULL;
    m_wc.lpszClassName = m_className.c_str();
	
	
}
/** destructor */
Wnd::~Wnd(void)
{
}

/** 
Create a new Window
@param[in]	pParent		Parent window 
*/
void Wnd::create(Wnd* pParent)
{
	assert(pParent);
	m_pParent = pParent;

	//there m_hWnd = NULL
	m_Wnds[m_hWnd] = this;

	HWND hWndParent = *m_pParent;

    m_hWnd = CreateWindow( m_className.c_str(),
		m_windowName.c_str(),
		m_style,
		m_x,
		m_y,
		m_width, m_height,
		hWndParent, NULL, NULL, NULL);

    if (!m_hWnd) 
		WndError();

	m_Wnds.erase( NULL );
	m_Wnds[m_hWnd] = this;
}

/**
Create a new main Window
call this function in WinMain input function
@param[in]	lpCmdLine	parameter from WinMain function
@param[in]	nCmdShow	parameter from WinMain function
*/
void Wnd::create(HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow)
{
	assert(hInstance);
	g_hInstance = hInstance;
	//g_hInstance = nCmdShow;

    if(!RegisterClass(&m_wc)) 
		WndError();

	//there m_hWnd = NULL
	m_Wnds[m_hWnd] = this;

	HWND hWndParent = NULL;
	if ( m_pParent )
		hWndParent = *m_pParent;

    m_hWnd = CreateWindow( m_className.c_str(),
		m_windowName.c_str(),
		m_style,
		m_x,
		m_y,
		m_width, m_height,
		hWndParent, NULL, NULL, NULL);

    if (!m_hWnd) 
		WndError();


    ShowWindow(m_hWnd, nCmdShow);
    UpdateWindow(m_hWnd);

}

/**
Infity loop function call this function 
in WinMain after created Main Wnd
*/
int Wnd::pumpMessage()
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

/**
Global callBack function for management Windows Message
Don't call directly this function
@param[in]	hwnd	windows handle
@param[in]	uMsg	Windows message indetificator
@param[in]	wParam	windows message's first parameter
@param[in]	wParam	windows message's second parameter
*/
LRESULT CALLBACK Wnd::globalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	Wnd *pWnd = m_Wnds[hwnd];

	/*
	if ( uMsg == WM_HOOK )
		return pWnd->onHook( lParam );
	*/
    switch (uMsg)
    {
        case WM_CREATE:
			//retreive NULL Window handle
			pWnd = m_Wnds[NULL];
			assert(pWnd);
			m_Wnds.erase( NULL );
			m_Wnds[hwnd] = pWnd;
			pWnd->setHandle( hwnd );
			return pWnd->onCreate();
            
		case WM_SIZE:
			return pWnd->onSize(LOWORD(lParam), HIWORD(lParam));

        case WM_DESTROY:
			return pWnd->onDestroy();
        
		case WM_COMMAND:
			return pWnd->onCommand( LOWORD(wParam) );

		case WM_CHANGECBCHAIN:
			return pWnd->onChangeCbChain( (HWND)lParam );

		case WM_DRAWCLIPBOARD:
			return pWnd->onDrawClipBoard();

		case WM_MOUSEMOVE:
		{
			int x = LOWORD(lParam);
			int y = HIWORD(lParam);
			Point point( LOWORD(lParam), HIWORD(lParam) );
			return pWnd->onMouseMove( point );
		}

		case WM_LBUTTONUP:
		{
			POINTS p = MAKEPOINTS(lParam);
			return pWnd->onLButtonUp( Point(p.x, p.y), wParam );
		}

		case WM_PAINT:
		{			
			//PAINTSTRUCT ps;
			//HDC hDC = ::BeginPaint(hwnd, &ps);
			//int ret = pWnd->onPaint( hDC );
			//::EndPaint(hwnd, &ps);
			PaintDc paintDc( pWnd );
			return pWnd->onPaint( &paintDc );
		}

        default:
			if ( pWnd )
				return pWnd->wndProc( uMsg, wParam, lParam );		
			else
				return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

}

/**
Default Windows Proc Message handler
@param[in]	uMsg	windows message
@param[in]	uMsg	Windows message indetificator
@param[in]	wParam	windows message's first parameter
@param[in]	wParam	windows message's second parameter
*/
LRESULT CALLBACK Wnd::wndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch ( uMsg )
	{
	case WM_CHANGECBCHAIN :
		assert(m_hWndNextViewer);
		// Pass the message to the next window in clipboard 
		// viewer chain. 
		SendMessage(m_hWndNextViewer, uMsg, wParam, lParam); 
		break;
	}
	return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}


/*@return global Handle instance application */
HINSTANCE Wnd::getHandleInstance(void)
{
	assert( g_hInstance );
	return g_hInstance;
}

/**
Call when Window was created
*/
int Wnd::onCreate()
{
	return 0;
}

/**
Call when Window was destroyed
*/
int Wnd::onDestroy()
{
	PostQuitMessage(0);
	return 0;
}

/**
Call when Window received a command message
@param[in]	command		command identifer
*/
int Wnd::onCommand(int command)
{
	return 0;
}

int Wnd::onChangeCbChain(HWND hWnd)
{
	m_hWndNextViewer = hWnd;
	return 0;
}

int Wnd::onDrawClipBoard()
{
	return 0;
}

/**
Call when Window was resized
@param[in]	width		new window's width 
@param[in]	height		new window's height 
*/
int Wnd::onSize(int width, int height)
{
	return 0;
}

int Wnd::onPaint(Dc *pDc)
{
	return 0;
}

int Wnd::onHook(LPARAM lParam)
{
	return 0;
}

/** On mouse moving
@param[in]	point	coordonate mouse pointer */
int Wnd::onMouseMove(Point point)
{
	return 0;
}

/** The WM_LBUTTONUP message is posted when the user releases the left mouse button
@param[in]	point	coordonate mouse pointer
@param[in]	wParam	Indicates whether various virtual keys are down */
int Wnd::onLButtonUp(Point point, WPARAM wParam)
{
	return 0;
}

/**
Function for moving windows
@param[in]	x		top left position 
@param[in]	y		top position
*/
void Wnd::move(int x, int y, int width, int height, bool repaint)
{
	assert(m_hWnd);
	if( !::MoveWindow( m_hWnd, x, y, width, height, repaint ) )
		throw WndError();
}
/**
Function for moving windows
@param[in]	rect	New coordonnate
*/
void Wnd::move(const Rect rect, bool repaint)
{
	assert(m_hWnd);
	if( !::MoveWindow( m_hWnd, rect.t.x, rect.t.y, rect.d.x, rect.d.y, repaint ) )
		throw WndError();
}

/** 
@return window's handle
HWND Wnd::getHandle() const 
{
	assert(m_hWnd);
	return m_hWnd; 
} */

/** 
@return window's handle */
Wnd::operator HWND(void) const
{
	assert(m_hWnd);
	return m_hWnd; 
}

/** Set text for window
@param[in]	text	text to set */
void Wnd::setText(const STRING text)
{
	assert(m_hWnd);
	if ( ::SetWindowText( m_hWnd, text.c_str() ) == FALSE )
		throw WndError();
}

/** Show Windows
@param[in]	nCmdShow	command see MSDN ShowWindow
@return true if ok */
bool Wnd::showWindow(int nCmdShow)
{
	assert(m_hWnd);
	return ( ::ShowWindow( m_hWnd, nCmdShow ) == TRUE );
}

/** set window's position
@param[in]	hWndInsertAfter		see MSDN
@param[in]	x	coordonnate
@param[in]	y	coordonnate
@param[in]	cx	coordonnate
@param[in]	cy	coordonnate */
bool Wnd::setWindowPos( HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags )
{
	assert(m_hWnd);
	return ( ::SetWindowPos( m_hWnd, hWndInsertAfter, x, y, cx, cy, uFlags ) == TRUE );
}

/** retreive window client rect coordonnate
@return coordonnate */
Rect Wnd::getClientRect(void) const
{
	assert(m_hWnd);
	RECT rect;
	assert( ::GetClientRect( m_hWnd, &rect ) == TRUE );
	return Rect(rect);
}

Rect Wnd::getWindowRect(void) const
{
	assert(m_hWnd);
	RECT rect;
	assert( ::GetWindowRect( m_hWnd, &rect ) == TRUE );
	return Rect( rect );
}

Point Wnd::screenToClient(const Point point) const
{
	assert(m_hWnd);
	POINT p = (POINT)point;
	assert( ::ScreenToClient( m_hWnd, &p ) );
	return Point( p );
}

Point Wnd::clientToScreen(const Point point) const
{
	assert(m_hWnd);
	POINT p = (POINT)point;
	assert( ::ClientToScreen( m_hWnd, &p ) );
	return Point( p );
}

int Wnd::messageBox( const STRING text, const STRING caption /*= "Error"*/, const UINT uType /*= 0*/ )
{
	assert(m_hWnd);
	return ::MessageBox( m_hWnd, text.c_str(), caption.c_str(), uType );
}

} //End namespace Gui
